# -*- coding: utf-8 -*-
# @Time    : 2024/7/4 13:47
# @Author  : yujiahao
# @File    : 01_fastapi_type_hinting.py
# @description:类型提示


"""
在 FastAPI 中使用类型提示¶


Pydantic 模型¶

    Pydantic 是一个用来执行数据校验的 Python 库。

    你可以将数据的"结构"声明为具有属性的类。 每个属性都拥有类型。

    接着你用一些值来创建这个类的实例，这些值会被校验，并被转换为适当的类型（在需要的情况下），返回一个包含所有数据的对象。

    然后，你将获得这个对象的所有编辑器支持。

    下面的例子来自 Pydantic 官方文档：

        from pydantic import BaseModel

        class Person(BaseModel):
            name: str
            age: int

        def get_person_name(one_person: Person):
            return one_person.name

        # 创建一个 Person 实例
        person = Person(name="John Doe", age=30)
        print(person)
        print(get_person_name(person))

    在这个示例中：
        - `Person` 类定义了数据结构，包含 `name` 和 `age` 属性。
        - 创建 `Person` 实例时，Pydantic 会校验数据，并在需要时进行类型转换。
        - 你将获得 `Person` 对象的所有编辑器支持，如代码补全和类型检查。

    整个 FastAPI 建立在 Pydantic 的基础之上。

    实际上你将在 教程 - 用户指南 看到很多这种情况。

    想进一步了解 Pydantic，请阅读其文档：https://pydantic-docs.helpmanual.io/

    通过这种方式，Pydantic 让你在编写代码时更加高效，同时确保了数据的可靠性和一致性。



FastAPI 利用这些类型提示来做下面几件事：

    1. 编辑器支持：
       - 提供代码补全和文档提示，提升开发效率。

    2. 类型检查：
       - 在开发时进行静态类型检查，减少运行时错误。

    FastAPI 还会用这些类型声明来：

    1. 定义参数要求：
       - 声明对请求路径参数、查询参数、请求头、请求体、依赖等的要求。

    2. 转换数据：
       - 将来自请求的数据自动转换为需要的类型。

    3. 校验数据：
       - 对于每一个请求，确保数据符合预期类型和格式。
       - 当数据校验失败时，自动生成错误信息返回给客户端。

    4. 使用 OpenAPI 记录 API：
       - 自动生成 OpenAPI 规范文档。
       - 用于自动生成交互式文档的用户界面，如 Swagger UI 和 ReDoc。

    通过使用标准的 Python 类型，只需要在一个地方声明（而不是添加更多的类、装饰器等），FastAPI 会为你完成很多的工作。

        例如：

        from fastapi import FastAPI
        from pydantic import BaseModel

        app = FastAPI()

        class Item(BaseModel):
            name: str
            description: str = None
            price: float
            tax: float = None

        @app.post("/items/")
        async def create_item(item: Item):
            return item

    在这个示例中：
    - FastAPI 会自动将请求体转换为 `Item` 类型的实例。
    - 校验数据是否符合 `Item` 的定义。
    - 如果数据无效，FastAPI 会返回一个详细的错误响应。
    - 自动生成 API 文档，展示在 /docs 和 /redoc 路径下。

    通过这种方式，FastAPI 让你在编写代码时更加高效，同时确保了 API 的可靠性和可维护性。
"""
from fastapi import FastAPI

# 看一个例子，用标准的Python语法编写一个函数

'''
这个函数做了下面这些事情：
    - 接收 first_name 和 last_name 参数。
    - 通过 title() 将每个参数的第一个字母转换为大写形式。
    - 中间用一个空格来拼接它们。
'''
from typing import List, Tuple, Set, Dict


def get_full_name_python(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    print(full_name)


# 但是在方法调用中你忘记了一些方法，然后你尝试向程序员老手的朋友——编辑器自动补全寻求帮助。
# 输入函数的第一个参数 first_name，输入点号（.）然后敲下 Ctrl+Space 来触发代码补全。
# 但遗憾的是并没有起什么作用，所以这里我们可以修改一下，可以给参数加上类型： 参数：类型
# 这样如果在同样的地方，通过 Ctrl+Space 触发自动补全，你会发现，一下就有提示了

def get_full_name_fastapi(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


# 还有其他类型也都可以使用，只要是所有的标准的Python类型都可以
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_d, item_e


# 嵌套类型¶
# 有些容器数据结构可以包含其他的值，比如 dict、list、set 和 tuple。它们内部的值也会拥有自己的类型。
# 可以使用 Python 的 typing 标准库来声明这些类型以及子类型。它专门用来支持这些类型提示。


# 列表¶
# 例如，让我们来定义一个由 str 组成的 list 变量。
# 从 typing 模块导入 List（注意是大写的 L）, 由于列表是带有"子类型"的类型，所以我们把子类型(元素)放在方括号中：

def process_item_list(items: List[str]):
    for item in items:
        print(item)


# 元组和集合¶
# 声明 tuple 和 set 的方法也是一样的：
# 变量 items_t 是一个 tuple，其中的前两个元素都是 int 类型, 最后一个元素是 str 类型。
# 变量 items_s 是一个 set，其中的每个元素都是 bytes 类型。
def process_items_tuple_set(items_t: Tuple[int, int, str], items_s: Set[bytes]):
    return items_t, items_s


# 字典¶
# 定义 dict 时，需要传入两个子类型，用逗号进行分隔。
# 第一个子类型声明 dict 的所有键。
# 第二个子类型声明 dict 的所有值：
# 变量 prices 是一个 dict：
# 这个 dict 的所有键为 str 类型（可以看作是字典内每个元素的名称）。
# 这个 dict 的所有值为 float 类型（可以看作是字典内每个元素的价格）。

def process_items_dict(prices: Dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)


# 类作为类型¶
# 你也可以将类声明为变量的类型。
# 假设你有一个名为 Person 的类，拥有 name 属性：
class Person:
    def __init__(self, name: str):
        self.name = name


# 可以将一个变量声明为 Person 类型
def get_person_name(one_person: Person):
    return one_person.name


def main():
    get_full_name_python("john", "doe")
    get_full_name_fastapi("john", "doe")
    process_item_list([1, 2, 3])


if __name__ == '__main__':
    main()
